import 'package:archive/archive_io.dart';
import 'dart:convert';
import 'dart:io';

class IconfontUtil {
  static Future<String> httpRead(String url) async {
    var newUrl = url;
    if (!newUrl.startsWith('https')) {
      newUrl = 'https:$url';
    }
    var httpClient = HttpClient();
    String result = '';
    try {
      var request = await httpClient.getUrl(Uri.parse(newUrl));
      var response = await request.close();
      if (response.statusCode == HttpStatus.ok) {
        result = await response.transform(utf8.decoder).join();
      }
    } catch (exception) {}
    return result;
  }

  static String netJSSvgParse(String input, RegExp exp) {
    Iterable<Match> matches = exp.allMatches(input);
    String output = '';
    for (Match m in matches) {
      String match = m.group(0)!;
      output += match;
    }
    return output;
  }

  static List<String> netJSSvgParseList(String input, RegExp exp) {
    Iterable<Match> matches = exp.allMatches(input);
    List<String> output = [];
    for (Match m in matches) {
      String match = m.group(0)!;
      output.add(match);
    }
    return output;
  }

  static String buildIconName(List<Map<String, dynamic>> svgList) {
    var sb = StringBuffer();
    svgList.forEach((element) {
      sb.writeln('''  ${element['name'].toUpperCase()},''');
    });
    return sb.toString();
  }

  static String buildSvgCase(List<Map<String, dynamic>> svgList) {
    var sb = StringBuffer();
    svgList.forEach((element) {
      var key = element['name'];
      var values = element['values'];
      var viewBox = element['viewBox'];
      sb.writeln("""      case IconFontName.${key.toUpperCase()}:""");
      sb.writeln("""        svgXml = '''""");
      sb.writeln(
          """          <svg viewBox="$viewBox" xmlns="http://www.w3.org/2000/svg">""");
      for (int i = 0; i < values.length; i++) {
        String path = values[i];
        // 取出path中的fill
        String fillColor =
            IconfontUtil.netJSSvgParse(path, RegExp(r'(?<=fill=").*?(?=")'));
        if (fillColor.isNotEmpty) {
          path = path.replaceAll(
              fillColor, "\${getColor($i, color, colors, '$fillColor')}");
        } else {
          path = path.replaceAll('''fill=""''', '');
          path = path.replaceAll("""fill=''""", '');
          path = path.replaceAllMapped(RegExp(r'></path>'), (match) {
            return ''' fill="\${getColor($i, color, colors, '$fillColor')}"></path>''';
          });
        }
        sb.writeln("""            $path""");
      }
      sb.writeln("""          \</svg\>""");
      sb.writeln("""        ''';""");
      sb.writeln("""        break;""");
    });
    return sb.toString();
  }

  static Future<List<Map<String, dynamic>>> parse2SvgList(
      String url, String prefix) async {
    List<Map<String, dynamic>> svgList = [];
    String result = await httpRead(url);
    if (result.isNotEmpty) {
      // 第一步取出<svg></svg> 标签对里内容
      String parse1 =
          netJSSvgParse(result, RegExp(r'(?<=\<svg\>).*?(?=\</svg\>)'));
      if (parse1.isNotEmpty) {
        // 第二步取出<symbol></symbol> 标签对
        List<String> parse2 =
            netJSSvgParseList(parse1, RegExp(r'\<symbol(.*?)\</symbol\>'));
        // 第三步取出name, 取出path
        parse2.forEach((element) {
          String name =
              netJSSvgParse(element, RegExp('''(?<=id="$prefix).*?(?=")'''));
          // 处理非法字符
          name =
              name.replaceAllMapped(RegExp(r'[^a-zA-Z0-9_]'), (match) => '_');
          String viewBox =
              netJSSvgParse(element, RegExp(r'''(?<=viewBox=").*?(?=")'''));
          List<String> pathList =
              netJSSvgParseList(element, RegExp(r'\<path(.*?)\</path\>'));
          svgList.add({'name': name, 'values': pathList, 'viewBox': viewBox});
        });
      }
    }
    return svgList;
  }
}

class IconfontAnimUtil {
  static final separator = Platform.pathSeparator;

  static Future<List<ParsPathModel>> read(
      {String? url,
      String? filePath,
      String? fileName,
      required String prefix,
      String defFillColor = '#00000000'}) async {
    List<ParsPathModel> svgList = [];
    String result = "";
    if (url != null && url.isNotEmpty) {
      try {
        result = await _httpRead(url);
      } catch (e) {}
    }
    if (filePath != null && filePath.isNotEmpty) {
      try {
        String suffix = filePath.split(".").last.toLowerCase();
        String path =
            "${Directory.current.path}$separator${filePath.split("/").join("/")}";
        if (suffix == "js") {
          result = await File(path).readAsString();
        }
        if (suffix == "zip") {
          final inputStream = InputFileStream(path);
          final archive = ZipDecoder().decodeBuffer(inputStream);
          for (ArchiveFile archiveFile in archive.files) {
            if (archiveFile.isFile &&
                fileName!.toLowerCase() ==
                    archiveFile.name.split("/").last.toLowerCase()) {
              // final outputStream = OutputFileStream(archiveFile.name);
              // archiveFile.writeContent(outputStream);
              // outputStream.close();

              // File file = File(archiveFile.name);
              // result = file.readAsStringSync();
              // file.deleteSync();
              result = utf8.decode(archiveFile.content);
              break;
            }
          }
        }
      } catch (e) {}
    }
    if (result.isNotEmpty) {
      // 第一步取出<svg></svg> 标签对里内容
      String parse1 =
          _netJSSvgParse(result, RegExp(r'(?<=\<svg\>).*?(?=\</svg\>)'));
      if (parse1.isNotEmpty) {
        // 第二步取出<symbol></symbol> 标签对
        List<String> parse2 =
            _netJSSvgParseList(parse1, RegExp(r'\<symbol(.*?)\</symbol\>'));
        // 第三步取出name, 取出path
        parse2.forEach((element) {
          // 处理非法name字符
          String name =
              _netJSSvgParse(element, RegExp('''(?<=id="$prefix).*?(?=")'''))
                  .replaceAllMapped(RegExp(r'[^a-zA-Z0-9_]'), (match) => '_');
          String viewBox =
              _netJSSvgParse(element, RegExp(r'''(?<=viewBox=").*?(?=")'''));
          List<double> viewBoxList =
              _parseList(viewBox, RegExp(r'''\d+(\.\d+)?'''));
          List<String> pathList = _netJSSvgParseList(
              element, RegExp(r'(\<path d=").*?(\</path\>)'));
          // 每一条path 生成独立数据
          List<PathInfoModel> pathDataMap = [];
          if (pathList.isNotEmpty) {
            pathDataMap = pathList.map((pathXml) {
              String path = _netJSSvgParse(
                  pathXml, RegExp(r'''(?<=\<path d=").*?(?=")'''));
              String fill =
                  _netJSSvgParse(pathXml, RegExp(r'''(?<=fill=").*?(?=")'''));
              String opacity = _netJSSvgParse(
                  pathXml, RegExp(r'''(?<=opacity=").*?(?=")'''));
              String fillColor = defFillColor;
              double opacityD = 1;
              if (fill.isNotEmpty) {
                fillColor = fill;
              }
              if (opacity.isNotEmpty) {
                opacityD = double.tryParse(opacity)!;
              }
              return PathInfoModel(
                  path: path, fillColor: fillColor, opacity: opacityD);
            }).toList();
          }
          var viewBoxModel = ViewBoxModel(
              minX: viewBoxList[0],
              minY: viewBoxList[1],
              width: viewBoxList[2],
              height: viewBoxList[3]);
          svgList.add(ParsPathModel(
              name: name, pathList: pathDataMap, viewBox: viewBoxModel));
        });
      }
    }
    return svgList;
  }

  static String buildIconName(List<ParsPathModel> svgList) {
    var sb = StringBuffer();
    svgList.forEach((element) {
      sb.writeln('''  ${element.name.toUpperCase()},''');
    });
    return sb.toString();
  }

  static String buildSvgCase(List<ParsPathModel> svgList) {
    var sb = StringBuffer();
    svgList.forEach((element) {
      var key = element.name;
      var values = element.pathList;
      var viewBox = element.viewBox;
      sb.writeln("""      case IconFontName.${key.toUpperCase()}:""");
      sb.writeln("""        return ParsPathModel(""");
      sb.writeln("""          name: '$key',""");
      sb.writeln("""          pathList: [""");
      for (var i = 0; i < values.length; i++) {
        PathInfoModel child = values[i];
        sb.writeln("""            PathInfoModel(""");
        sb.writeln("""              path: '${child.path}',""");
        sb.writeln(
            """              fillColor: getColor($i, '${child.fillColor}'),""");
        sb.writeln(
            """              opacity: getOpacity($i, ${child.opacity}),""");
        sb.writeln("""            ),""");
      }
      sb.writeln("""          ],""");
      sb.writeln(
          """          viewBox: const ViewBoxModel(minX: ${viewBox.minX}, minY: ${viewBox.minY}, width: ${viewBox.width}, height: ${viewBox.height}),""");
      sb.writeln("""        );""");
    });
    return sb.toString();
  }

  static Future<String> _httpRead(String url) async {
    var newUrl = url;
    if (!newUrl.startsWith('https')) {
      newUrl = 'https:$url';
    }
    var httpClient = HttpClient();
    String result = '';
    try {
      var request = await httpClient.getUrl(Uri.parse(newUrl));
      var response = await request.close();
      if (response.statusCode == HttpStatus.ok) {
        result = await response.transform(utf8.decoder).join();
      }
    } catch (exception) {}
    return result;
  }

  static String _netJSSvgParse(String input, RegExp exp) {
    Iterable<Match> matches = exp.allMatches(input);
    String output = '';
    for (Match m in matches) {
      String match = m.group(0)!;
      output += match;
    }
    return output;
  }

  static List<String> _netJSSvgParseList(String input, RegExp exp) {
    Iterable<Match> matches = exp.allMatches(input);
    List<String> output = [];
    for (Match m in matches) {
      String match = m.group(0)!;
      output.add(match);
    }
    return output;
  }

  static List<double> _parseList(String input, RegExp exp) {
    Iterable<Match> matches = exp.allMatches(input);
    List<double> output = [];
    for (Match m in matches) {
      double match = double.tryParse(m.group(0)!)!;
      output.add(match);
    }
    return output;
  }
}

class ParsPathModel {
  final String name;
  final List<PathInfoModel> pathList;
  final ViewBoxModel viewBox;

  const ParsPathModel(
      {required this.name, required this.pathList, required this.viewBox});
}

class ViewBoxModel {
  final double minX;
  final double minY;
  final double width;
  final double height;

  const ViewBoxModel(
      {required this.minX,
      required this.minY,
      required this.width,
      required this.height});
}

class PathInfoModel {
  final String path;
  final String fillColor;
  final double opacity;

  const PathInfoModel(
      {required this.path, required this.fillColor, required this.opacity});
}
